package es.udc.pfc.davidparedes.model.storeservice;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.beans.factory.annotation.Autowired;

import es.udc.pfc.davidparedes.model.director.Director;
import es.udc.pfc.davidparedes.model.director.DirectorDao;
import es.udc.pfc.davidparedes.model.mission.Mission;
import es.udc.pfc.davidparedes.model.mission.MissionDao;
import es.udc.pfc.davidparedes.model.receptionist.Receptionist;
import es.udc.pfc.davidparedes.model.receptionist.ReceptionistDao;
import es.udc.pfc.davidparedes.model.shift.Shift;
import es.udc.pfc.davidparedes.model.shift.ShiftDao;
import es.udc.pfc.davidparedes.model.store.Store;
import es.udc.pfc.davidparedes.model.store.StoreDao;
import es.udc.pfc.davidparedes.model.task.Task;
import es.udc.pfc.davidparedes.model.task.TaskDao;
import es.udc.pfc.davidparedes.model.universe.Universe;
import es.udc.pfc.davidparedes.model.universe.UniverseDao;
import es.udc.pfc.davidparedes.model.universemanager.UniverseManager;
import es.udc.pfc.davidparedes.model.universemanager.UniverseManagerDao;
import es.udc.pfc.davidparedes.model.util.DuplicateInstanceException;
import es.udc.pfc.davidparedes.model.util.InstanceNotFoundException;
import es.udc.pfc.davidparedes.model.vendor.Vendor;
import es.udc.pfc.davidparedes.model.vendor.VendorDao;


@Service("storeService")
@Transactional
public class StoreServiceImpl implements StoreService {
	
	@Autowired
	private StoreDao storeDao;
	
	@Autowired
	private UniverseDao universeDao;
	
	@Autowired
	private UniverseManagerDao universeManagerDao;
	
	@Autowired
	private ShiftDao shiftDao;
	
	@Autowired
	private TaskDao taskDao;
	
	@Autowired
	private MissionDao missionDao;
	
	@Autowired
	private VendorDao vendorDao;
	
	@Autowired
	private ReceptionistDao receptionistDao;
	
	@Autowired
	private DirectorDao directorDao;

	public Store registerStore(String name, String address)
			throws DuplicateInstanceException {
		try {
			storeDao.findByName(name);
			throw new DuplicateInstanceException(name, Store.class.getName());
		} catch(InstanceNotFoundException e) {
			Store store = new Store(name, address);
			storeDao.save(store);
			return store;
		}
	}
	
	public void updateStore(Long storeId, String name, String address)
			throws InstanceNotFoundException {
		Store store = storeDao.find(storeId);
		store.setName(name);
		store.setAddress(address);
	}
	
	public void deleteStore(Long storeId)
			throws InstanceNotFoundException {
		
		Store store = storeDao.find(storeId);
		Calendar today = getToday();
		
		for (Universe universe : store.getUniverses()) {
			deleteUniverse(universe.getUniverseId());
		}
		for (Receptionist receptionist : receptionistDao.findReceptionistsByStore(store.getStoreId())) {
			receptionist.setContractTermination(today);
		}
		for (Vendor vendor : vendorDao.findVendorsByStore(store.getStoreId())) {
			vendor.setContractTermination(today);
		}
		for (UniverseManager manager : universeManagerDao.findUniverseManagersByStore(store.getStoreId())) {
			manager.setContractTermination(today);
		}
		for (Director director : directorDao.findDirectorsByStore(store.getStoreId())) {
			director.setDeletionDate(today);
		}
		store.setDeletionDate(today);
	}
	
	public Store findStore(long storeId)
			throws InstanceNotFoundException {
		return storeDao.find(storeId);
	}
	
	public Store findStoreByName(String name)
			throws InstanceNotFoundException {
		return storeDao.findByName(name);
	}

	public Universe registerUniverse(String name, long storeId)
			throws InstanceNotFoundException {
		Store store = storeDao.find(storeId);
		Universe universe = new Universe(name, store);
		universeDao.save(universe);
		return universe;
	}

	public void updateUniverse(Long universeId, String name,
			Long storeId) throws InstanceNotFoundException {
		Universe universe = universeDao.find(universeId);
		Store store = storeDao.find(storeId);
		universe.setName(name);
		universe.setStore(store);
	}
	
	public void deleteUniverse(Long universeId)
			throws InstanceNotFoundException {
		
		Universe universe = universeDao.find(universeId);
		for (Vendor vendor : universe.getVendors()) {
			vendor.setUniverse(null);
		}
		if (universe.getManager() != null) {
			universe.getManager().setUniverse(null);
			universe.setManager(null);
		}
		Calendar today = getToday();
		universe.getStore().removeUniverse(universe);
		universe.setDeletionDate(today);
	}
	
	public boolean setUniverseManager(long universeId, long managerId)
			throws InstanceNotFoundException {
		Universe universe = universeDao.find(universeId);
		UniverseManager manager = universeManagerDao.find(managerId);
		if (manager.getUniverse()==universe) {
			universe.setManager(manager);
			return true;
		}
		return false;
	}
	
	public Universe findUniverse(long universeId)
			throws InstanceNotFoundException {
		return universeDao.find(universeId);
	}
	
	public List<Mission> getMissionsByDate(Long storeId, Calendar start, Calendar end)
			throws InstanceNotFoundException {
		Store store = storeDao.find(storeId);
		return missionDao.findStoreMissionsByDate(store.getStoreId(), start, end);
	}
	
	public List<Mission> getCurrentMissions(Long storeId)
			throws InstanceNotFoundException {
		Store store = storeDao.find(storeId);
		Calendar current = Calendar.getInstance();
		return missionDao.findStoreMissionsAtMoment(store.getStoreId(), current);
	}

	public List<Shift> getUniverseShiftsByDate(Long universeId, Calendar start,
			Calendar end) throws InstanceNotFoundException {
		Universe universe = universeDao.find(universeId);
		return shiftDao.findUniverseShiftsByDate(universe.getUniverseId(), start, end);
	}
	
	public List<Shift> getStoreShiftsByDate(Long storeId, Calendar start, Calendar end)
			throws InstanceNotFoundException {
		Store store = storeDao.find(storeId);
		return shiftDao.findStoreShiftsByDate(store.getStoreId(), start, end);
	}
	
	public List<Shift> getCurrentShifts(Long universeId)
			throws InstanceNotFoundException {
		Universe universe = universeDao.find(universeId);
		Calendar current = Calendar.getInstance();
		return shiftDao.findUniverseShiftsAtMoment(universe.getUniverseId(), current);
	}

	public List<Task> getUniverseTasksByDate(Long universeId,
			Calendar start, Calendar end) throws InstanceNotFoundException {
		Universe universe = universeDao.find(universeId);
		return taskDao.findUniverseTasksByDate(universe.getUniverseId(), start, end);
	}

	public List<Task> getCurrentTasks(Long universeId)
			throws InstanceNotFoundException {
		Universe universe = universeDao.find(universeId);
		Calendar current = Calendar.getInstance();
		return taskDao.findUniverseTasksAtMoment(universe.getUniverseId(), current);
	}
	
	public List<UniverseCurrentActivityDTO> getCurrentActivity(Long storeId)
			throws InstanceNotFoundException {
		List<UniverseCurrentActivityDTO> dtoList = new ArrayList<UniverseCurrentActivityDTO>();
		
		Store store = storeDao.find(storeId);
		for (Universe universe : store.getUniverses()) {
			UniverseCurrentActivityDTO dto = new UniverseCurrentActivityDTO(universe, getCurrentTasks(universe.getUniverseId()), getCurrentShifts(universe.getUniverseId()));
			dtoList.add(dto);
		}
		return dtoList;
	}
	
	public List<UniverseRowStatsDTO> getUniverseStats(Long universeId)
    		throws InstanceNotFoundException {
    	
		UniverseRowStatsDTO row;
    	List<UniverseRowStatsDTO> result = new ArrayList<UniverseRowStatsDTO>();
    	
    	findUniverse(universeId);
    	Calendar[] dates = getStartEndDates();
    	
    	List<VendorAndMillisDTO> weekStats = taskDao.getUniverseTasksDuration(universeId, dates[0], dates[1]);
    	List<VendorAndMillisDTO> monthStats = taskDao.getUniverseTasksDuration(universeId, dates[2], dates[3]);
    	List<VendorAndMillisDTO> trimesterStats = taskDao.getUniverseTasksDuration(universeId, dates[4], dates[5]);
    	
    	for (VendorAndMillisDTO trimester : trimesterStats) {
    		row = new UniverseRowStatsDTO();
    		row.setVendor(trimester.getVendor());
    		if (trimester.getMillis() != null) {
    			row.setTrimesterMillis(trimester.getMillis());
    		} else {
    			row.setTrimesterMillis((long)0);
    		}
    		for (VendorAndMillisDTO month : monthStats) {
    			if (month.getVendor()==row.getVendor()) {
    				if (month.getMillis() != null) {
    				row.setMonthMillis(month.getMillis());
    				} else {
    					row.setMonthMillis((long) 0);
    				}
    			}
    		}
    		for (VendorAndMillisDTO week : weekStats) {
    			if (week.getVendor()==row.getVendor()) {
    				if (week.getMillis() != null) {
    					row.setWeekMillis(week.getMillis());
    				} else {
    					row.setWeekMillis((long) 0);
    				}
    			}
    		}
    		result.add(row);
    	}
    	
    	return result;
    }
	
	public List<Receptionist> getStoreReceptionists(Long storeId)
			throws InstanceNotFoundException {
		storeDao.find(storeId);
		return receptionistDao.findReceptionistsByStore(storeId);
	}
	
	public List<Vendor> getStoreVendors(Long storeId)
			throws InstanceNotFoundException {
		storeDao.find(storeId);
		return vendorDao.findVendorsByStore(storeId);
	}
	
	public List<UniverseManager> getStoreManagers(Long storeId)
			throws InstanceNotFoundException {
		storeDao.find(storeId);
		return universeManagerDao.findUniverseManagersByStore(storeId);
	}
	
	public List<StoreDirectorsDTO> getAllStoresAndDirectors() {
		
		ArrayList<StoreDirectorsDTO> storesDTO = new ArrayList<StoreDirectorsDTO>();
		StoreDirectorsDTO storeDTO = null;
		
		List<Store> stores = storeDao.findAll();
		for (Store store : stores) {
			if (store.getDeletionDate() == null) {
				storeDTO = new StoreDirectorsDTO(store, directorDao.findDirectorsByStore(store.getStoreId()));
				storesDTO.add(storeDTO);
			}
		}
		
		return storesDTO;
	}
	
	private Calendar[] getStartEndDates() {
		int year = Calendar.getInstance().get(Calendar.YEAR);
		int month = Calendar.getInstance().get(Calendar.MONTH);
		int week = Calendar.getInstance().get(Calendar.WEEK_OF_YEAR);
		
		Calendar startWeek = Calendar.getInstance();
		startWeek.clear();
		startWeek.set(Calendar.YEAR, year);
		startWeek.set(Calendar.WEEK_OF_YEAR, week);
		Calendar endWeek = (Calendar) startWeek.clone();
		endWeek.add(Calendar.WEEK_OF_YEAR, 1);
		
		Calendar startMonth = Calendar.getInstance();
		startMonth.clear();
		startMonth.set(Calendar.YEAR, year);
		startMonth.set(Calendar.MONTH, month);
		Calendar endMonth = (Calendar) startMonth.clone();
		endMonth.add(Calendar.MONTH, 1);
		
		switch (month) {
		case 0 : break;
		case 1 : month = 0; break;
		case 2 : month = 0; break;
		case 3 : break;
		case 4 : month = 3; break;
		case 5 : month = 3; break;
		case 6 : break;
		case 7 : month = 6; break;
		case 8 : month = 6; break;
		case 9 : break;
		case 10 : month = 9; break;
		case 11 : month = 9; break;
		}
		Calendar startTrimester = Calendar.getInstance();
		startTrimester.clear();
		startTrimester.set(Calendar.YEAR, year);
		startTrimester.set(Calendar.MONTH, month);
		Calendar endTrimester = (Calendar) startTrimester.clone();
		endTrimester.add(Calendar.MONTH, 3);
		
		Calendar[] dates = new Calendar[6];
		dates[0] = startWeek;
		dates[1] = endWeek;
		dates[2] = startMonth;
		dates[3] = endMonth;
		dates[4] = startTrimester;
		dates[5] = endTrimester;
		
		return dates;
	}
	
	private Calendar getToday() {
		
		Calendar today = Calendar.getInstance();
    	today.set(Calendar.HOUR_OF_DAY, 0);
    	today.set(Calendar.MINUTE, 0);
    	today.set(Calendar.SECOND, 0);
    	
    	return today;
	}

}
